package com.pink.config;

import com.pink.component.RedisComponent;
import com.pink.entity.dto.UserDto;
import com.pink.entity.vo.RestBeanVo;
import com.pink.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;

import com.pink.entity.vo.response.AuthorizeVO;
import com.pink.filter.JwtAuthorizeFilter;
import com.pink.utils.JwtUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.context.annotation.Bean;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

@Configuration
public class SecurityConfiguration{

    @Resource
    JwtUtils Utils;

    @Resource
    UserService service;

    @Resource
    RedisComponent redisComponent;

    @Resource
    JwtAuthorizeFilter jwtAuthorizeFilter;
    @Autowired
    private JwtUtils jwtUtils;

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    // 认证管理器配置
    @Bean
    public AuthenticationManager authenticationManager(
            UserDetailsService userDetailsService,
            PasswordEncoder passwordEncoder) {

        DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
        provider.setUserDetailsService(userDetailsService);
        provider.setPasswordEncoder(passwordEncoder);
        return new ProviderManager(provider);
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        return http
                .authorizeHttpRequests(conf -> conf
                        // 配置无需认证
                        .antMatchers("/**").permitAll()
                        .anyRequest().authenticated() // 其他认证
                )
                .formLogin(conf -> conf
                        .loginProcessingUrl("/api/account/login")
                        .usernameParameter("username")
                        .passwordParameter("password")
                        .failureHandler(this::onAuthenticationFailure)
                        .successHandler(this::onAuthenticationSuccess)
                )
                .logout(conf -> conf
                        .logoutUrl("/api/account/logout")
                        .logoutSuccessHandler(this::onLogoutSuccess)
                )
                .exceptionHandling(conf -> conf
                        .authenticationEntryPoint(this::onUnauthorized)
                        .accessDeniedHandler(this::onAccessDeny)
                )
                .csrf(AbstractHttpConfigurer::disable)
                .sessionManagement(conf->conf
                        .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                )
                .addFilterBefore(jwtAuthorizeFilter, UsernamePasswordAuthenticationFilter.class)
                .build();
    }

    /**
     *权限认证
     */
    public void onAccessDeny(HttpServletRequest request,
                             HttpServletResponse response,
                             AccessDeniedException exception) throws IOException {
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().write(RestBeanVo.forbidden(exception.getMessage()).asJsonString());
    }

    /**
     * 配置未登录
     */
    public void  onUnauthorized(HttpServletRequest request,
                                HttpServletResponse response,
                                AuthenticationException authException) throws IOException{
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().write(RestBeanVo.failure(authException.getMessage()).asJsonString());
    }

    /**
     * 配置登录成功
     */
    public void onAuthenticationSuccess(HttpServletRequest request,
                                        HttpServletResponse response,
                                        Authentication authentication) throws IOException {
        response.setContentType("application/json;charset=UTF-8");
        User user = (User) authentication.getPrincipal();
        UserDto account = service.findAccountByNameOrEmail(user.getUsername());
        String token = Utils.createJwt(user,account);
        AuthorizeVO vo = new AuthorizeVO();
        BeanUtils.copyProperties(account,vo);
        vo.setExpire(Utils.expirTime());
        vo.setAvatar(account.getAvatarUrl());
        vo.setToken(token);
        service.updateLastLoginTime(account.getUserId());
        response.getWriter().write(RestBeanVo.success(vo).asJsonString());
    }

    /**
     * 配置登录异常
     */
    public void onAuthenticationFailure(HttpServletRequest request,
                                        HttpServletResponse response,
                                        AuthenticationException exception) throws IOException {
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().write(RestBeanVo.failure(401,exception.getMessage()).asJsonString());
    }

    /**
     * 退出登录成功
     */
    public void onLogoutSuccess(HttpServletRequest request,
                                HttpServletResponse response,
                                Authentication authentication) throws IOException {
        response.setContentType("application/json;charset=UTF-8");
        PrintWriter writer = response.getWriter();
        String authorization = request.getHeader("Authorization");
        if(Utils.invalidateJwt(authorization)){
            writer.write(RestBeanVo.success().asJsonString());
        }else{
            writer.write(RestBeanVo.failure(400,"退出登录失败").asJsonString());
        }
    }
}
